package online.foxnull2.foxmall.ware.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import online.foxnull2.common.to.SkuInfoTo;
import online.foxnull2.common.to.mq.OrderTo;
import online.foxnull2.common.to.mq.StockDetailTo;
import online.foxnull2.common.to.mq.StockLockedTo;
import online.foxnull2.foxmall.ware.entity.WareOrderTaskDetailEntity;
import online.foxnull2.foxmall.ware.entity.WareOrderTaskEntity;
import online.foxnull2.foxmall.ware.entity.WareSkuEntity;
import online.foxnull2.foxmall.ware.enume.OrderStatusEnum;
import online.foxnull2.foxmall.ware.exception.StockInsufficientException;
import online.foxnull2.foxmall.ware.feign.OrderFeignService;
import online.foxnull2.foxmall.ware.feign.ProductFeignService;
import online.foxnull2.foxmall.ware.mapper.WareSkuMapper;
import online.foxnull2.foxmall.ware.service.WareOrderTaskDetailService;
import online.foxnull2.foxmall.ware.service.WareOrderTaskService;
import online.foxnull2.foxmall.ware.service.WareSkuService;
import online.foxnull2.foxmall.ware.utils.PageUtils;
import online.foxnull2.foxmall.ware.utils.Query;
import online.foxnull2.foxmall.ware.vo.wareSkuLock.OrderItem;
import online.foxnull2.foxmall.ware.vo.wareSkuLock.SkuWareHasStock;
import online.foxnull2.foxmall.ware.vo.wareSkuLock.WareSkuLockVo;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuMapper, WareSkuEntity> implements WareSkuService {

    private final ProductFeignService productFeignService;
    private final RabbitTemplate rabbitTemplate;
    private final WareOrderTaskService wareOrderTaskService;
    private final WareOrderTaskDetailService wareOrderTaskDetailService;
    private final OrderFeignService orderFeignService;

    @Autowired
    public WareSkuServiceImpl(ProductFeignService productFeignService,
                              RabbitTemplate rabbitTemplate,
                              WareOrderTaskService wareOrderTaskService,
                              WareOrderTaskDetailService wareOrderTaskDetailService,
                              OrderFeignService orderFeignService) {
        this.productFeignService = productFeignService;
        this.rabbitTemplate = rabbitTemplate;
        this.wareOrderTaskService = wareOrderTaskService;
        this.wareOrderTaskDetailService = wareOrderTaskDetailService;
        this.orderFeignService = orderFeignService;
    }

    @Transactional
    @Override
    public void stockReleaseHandle(StockLockedTo stockLockedTo) {
        StockDetailTo stockDetailTo = stockLockedTo.getStockDetailTo();
        Long detailId = stockDetailTo.getId();
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = wareOrderTaskDetailService.getById(detailId);
        if (!ObjectUtils.isEmpty(wareOrderTaskDetailEntity)) {
            Long taskId = stockLockedTo.getTaskId();
            WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getById(taskId);
            String orderSn = wareOrderTaskEntity.getOrderSn();
            Integer status = orderFeignService.getOrderStatusByOrderSn(orderSn);
            if (ObjectUtils.isEmpty(status) || Objects.equals(status, OrderStatusEnum.CANCLED.getCode())) {
                Long skuId = wareOrderTaskDetailEntity.getSkuId();
                Long wareId = wareOrderTaskDetailEntity.getWareId();
                Integer count = wareOrderTaskDetailEntity.getSkuNum();
                // stock release
                Integer lockStatus = wareOrderTaskDetailEntity.getLockStatus();
                if (lockStatus == 1) {
                    stockRelease(skuId, wareId, count, detailId);
                }
            }
        }
    }

    @Transactional
    @Override
    public void stockReleaseHandle(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();
        Integer status = orderFeignService.getOrderStatusByOrderSn(orderSn);
        if (ObjectUtils.isEmpty(status) || Objects.equals(status, OrderStatusEnum.CANCLED.getCode())) {
            WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskService.getWareOrderTaskByOrderSn(orderSn);
            Long taskId = wareOrderTaskEntity.getId();
            List<WareOrderTaskDetailEntity> wareOrderTaskDetailEntityList = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                    .eq("task_id", taskId).eq("lock_status", 1));
            for (WareOrderTaskDetailEntity wareOrderTaskDetailEntity : wareOrderTaskDetailEntityList) {
                Long skuId = wareOrderTaskDetailEntity.getSkuId();
                Long wareId = wareOrderTaskDetailEntity.getWareId();
                Integer count = wareOrderTaskDetailEntity.getSkuNum();
                Long detailId = wareOrderTaskDetailEntity.getId();
                stockRelease(skuId, wareId, count, detailId);
            }
        }
    }

    private void stockRelease(Long skuId, Long wareId, Integer count, Long detailId) {
        baseMapper.stockRelease(skuId, wareId, count);
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(detailId)
                .setLockStatus(2);
        wareOrderTaskDetailService.updateById(wareOrderTaskDetailEntity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        Object skuId = params.get("skuId");
        if (!ObjectUtils.isEmpty(skuId)) {
            queryWrapper.eq("sku_id", skuId);
        }
        Object wareId = params.get("wareId");
        if (!ObjectUtils.isEmpty(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Integer skuNum, Long skuId, Long wareId) {
        WareSkuEntity wareSkuEntity = this.getOne(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (ObjectUtils.isEmpty(wareSkuEntity)) {
            SkuInfoTo skuInfoTo = productFeignService.infoTo(skuId);
            this.save(new WareSkuEntity()
                    .setSkuId(skuId)
                    .setWareId(wareId)
                    .setStock(skuNum)
                    .setSkuName(skuInfoTo.getSkuName())
                    .setStockLocked(0));
        } else {
            wareSkuEntity.setStock(wareSkuEntity.getStock() + skuNum);
            this.updateById(wareSkuEntity);
        }
    }

    @Override
    public Set<Long> getSkuHasStock(List<Long> skuIds) {
        Set<Long> collect = skuIds.stream().filter(skuId -> {
            Long stock = baseMapper.hasStock(skuId);
            return !ObjectUtils.isEmpty(stock) && 0 < stock;
        }).collect(Collectors.toSet());
        log.debug("getSkuHasStock -> {}", collect);
        return collect;
    }

    @Transactional(rollbackFor = StockInsufficientException.class)
    @Override
    public void orderSkuLockStock(WareSkuLockVo wareSkuLockVo) {
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(wareSkuLockVo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);
        Long taskId = wareOrderTaskEntity.getId();
        List<OrderItem> orderItems = wareSkuLockVo.getOrderItems();
        List<SkuWareHasStock> skuWareHasStocks = orderItems.stream().map(item -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            skuWareHasStock.setSkuId(skuId);
            List<Long> wareIds = baseMapper.listWareIdBySkuId(skuId);
            skuWareHasStock.setWareIds(wareIds);
            Integer count = item.getCount();
            skuWareHasStock.setCount(count);
            return skuWareHasStock;
        }).collect(Collectors.toList());
        for (SkuWareHasStock skuWareHasStock : skuWareHasStocks) {
            boolean lock = false;
            Long skuId = skuWareHasStock.getSkuId();
            List<Long> wareIds = skuWareHasStock.getWareIds();
            if (ObjectUtils.isEmpty(wareIds)) {
                throw new StockInsufficientException(skuId);
            }
            Integer count = skuWareHasStock.getCount();
            for (Long wareId : wareIds) {
                Integer res = baseMapper.lockSkuStock(skuId, wareId, count);
                if (1 == res) {
                    lock = true;
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetailEntity.setSkuId(skuId)
                            .setSkuNum(count)
                            .setWareId(wareId)
                            .setTaskId(taskId)
                            .setLockStatus(1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                    StockDetailTo stockDetailTo = new StockDetailTo();
                    stockDetailTo.setId(wareOrderTaskDetailEntity.getId())
                            .setSkuId(wareOrderTaskDetailEntity.getSkuId())
                            .setSkuName(wareOrderTaskDetailEntity.getSkuName())
                            .setSkuNum(wareOrderTaskDetailEntity.getSkuNum())
                            .setTaskId(wareOrderTaskDetailEntity.getTaskId())
                            .setWareId(wareOrderTaskDetailEntity.getWareId())
                            .setLockStatus(wareOrderTaskDetailEntity.getLockStatus());
                    StockLockedTo stockLockedTo = new StockLockedTo();
                    stockLockedTo.setTaskId(taskId)
                            .setStockDetailTo(stockDetailTo);
                    rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", stockLockedTo);
                    break;
                }
            }
            if (Boolean.FALSE.equals(lock)) {
                throw new StockInsufficientException(skuId);
            }
        }
    }
}